#include "public_includes.h"
#include "clip_list.h"
#include "cex.h"
#include "private_includes.h"

struct _vus_clip_list_node;
typedef struct _vus_clip_list_node vus_clip_list_node;

typedef struct _vus_clip_list_node {
	vus_clip_list_node *previous, *next;
	vus_clip_base *value;
} vus_clip_list_node;

typedef struct _vus_clip_list {
	uint count;
	vus_clip_list_node *head, *tail;
} vus_clip_list;

#ifdef NDEBUG
#define invariant(self)
#else
#define invariant(self) \
	assert(self != NULL); \
	assert(self->count >= 0); \
	if (self->count > 0) \
		assert(self->head != NULL); \
	else \
		assert(self->head == NULL);
#endif

vus_clip_list *vus_clip_list_alloc() {
	vus_clip_list *output = (vus_clip_list *)malloc(sizeof(vus_clip_list));

	if (output == NULL)
		THROW(VUS_OUT_OF_MEMORY, "Out of memory.");
	
	output->count = 0;
	output->head = NULL;

	invariant(output);
	return output;
}

void vus_clip_list_free(vus_clip_list *self) {
	assert(self != NULL);
	vus_clip_list_clear(self);
	free(self);
}

void vus_clip_list_add(vus_clip_list *self, vus_clip_base *value) {
	vus_clip_list_node *node;
	assert(self != NULL);
	assert(value != NULL);

	node = (vus_clip_list_node *)malloc(sizeof(vus_clip_list_node));

	if (node == NULL)
		THROW(VUS_OUT_OF_MEMORY, "Out of memory.");

	node->previous = node->next = NULL;
	node->value = value;

	// Null head means empty list: therefore head and tail are the same.
	if (self->head == NULL)
		self->head = self->tail = node;
	else {
		self->tail->next = node;
		node->previous = self->tail;

		self->tail = node;
	}

	++self->count;

	invariant(self);
}

void vus_clip_list_remove(vus_clip_list *self, vus_clip_base *value) {
	vus_clip_list_node *node;
	assert(self != NULL);
	assert(value != NULL);

	node = self->head;
	while (node != NULL) {
		if (node->value == value) {
			// Link previous and next.
			if (node->previous != NULL)
				node->previous = node->next;
			if (node->next != NULL)
				node->next = node->previous;

			// Update head and tail if neccessary.
			if (node == self->head)
				self->head = node->next;
			if (node == self->tail)
				self->tail = node->previous;

			free(node);
			--self->count;

			break;
		}
	}

	invariant(self);
}

void vus_clip_list_clear(vus_clip_list *self) {
	assert(self != NULL);
	
	// TODO: Inline/Optimize
	while (self->head != NULL)
		vus_clip_list_remove(self, self->head->value);

	invariant(self);
}

uint vus_clip_list_count(vus_clip_list *self) {
	assert(self != NULL);

	return self->count;
}

vus_clip_base *vus_clip_list_get(vus_clip_list *self, uint index) {
	vus_clip_list_node *output;
	uint i;

	assert(self != NULL);
	assert(index < self->count);

	// Because index must be valid (asserted above) we can start at the head.
	// Because we start at the head, the next loop can start at one (head == index 0 right?)
	output = self->head;
	for (i = 1; i < self->count; i++)
		output = output->next;

	return output->value;
}

void vus_clip_list_set(vus_clip_list *self, uint index, vus_clip_base *value) {
	assert(self != NULL);
	assert(index < self->count);
	assert(value != NULL);

	// TODO: Can this be more efficient?
	vus_clip_list_remove(self, vus_clip_list_get(self, index));
	vus_clip_list_add(self, value);

	invariant(self);
}

int vus_clip_list_index_of(vus_clip_list *self, vus_clip_base *value) {
	uint output;
	vus_clip_list_node *node;

	assert(self != NULL);
	assert(value != NULL);

	output = 0;
	node = self->head;

	while (node != NULL) {
		if (node->value == value)
			return output;
		
		++output;
		node = node->next;
	}

	return -1;
}
